
package com.quiz.common.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.quiz.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
@SuppressWarnings("all")
public class RequestLogFilter implements Filter {

    private final StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
    // 保留原有日志对象和基本结构

    //    @SuppressWarnings("all")
    private void logRequest(HttpServletRequest request) throws IOException {
        StringBuilder requestLog = new StringBuilder()
                .append("\n=============== Request =================\n")
                .append(request.getMethod() + " ").append(request.getRequestURI()).append("\n");
        StringBuilder paramLog = new StringBuilder();

        // 1. 获取查询参数（URL参数）
        UriComponentsBuilder uriBuilder = UriComponentsBuilder
                .fromUriString(request.getRequestURI());
        Map<String, List<String>> queryParams = uriBuilder.build()
                .getQueryParams();
        if (!queryParams.isEmpty()) {
            paramLog.append("Query Params:\n");
            queryParams.forEach((k, v) -> paramLog.append("  ").append(k).append("=").append(String.join(",", v)).append("\n"));
        }

        // 2. 获取表单参数或文件上传信息
        if (isMultipartRequest(request)) {
            paramLog.append("Multipart Request (File Upload)\n");
            // 如果是文件上传请求，不尝试读取请求体
        } else {
            Map<String, String[]> formParams = request.getParameterMap();
            if (!formParams.isEmpty()) {
                paramLog.append("Form Params:\n");
                formParams.forEach((k, v) -> paramLog.append("  ").append(k).append("=").append(Arrays.toString(v)).append("\n"));
            }

            // 3. 获取请求体内容（非文件上传请求）
            String requestBody = getRequestBody(request);
            if (StringUtils.isNotEmpty(requestBody)) {
                paramLog.append("Request Body:\n  ").append(requestBody);
            }
        }

        // 合并到日志输出
        if(StringUtils.isEmpty(paramLog)){
            requestLog.append("No Find Params").append("\n");
        }else {
            requestLog.append(paramLog.toString()).append("\n");
        }

        requestLog.append("===================================================\n");
        log.info("\033[36m" + new String(requestLog.toString().getBytes(), "UTF-8") + "\033[0m");
    }

    private boolean isMultipartRequest(HttpServletRequest request) {
        return multipartResolver.isMultipart(request);
    }

    private String getRequestBody(HttpServletRequest request) throws IOException {
        if (isMultipartRequest(request)) {
            return "[MULTIPART/FILE UPLOAD CONTENT]";
        }
        return ((RepeatedlyRequestWrapper) request).getBodyString();
    }


    private void logResponse(ContentCachingResponseWrapper response, String time) throws UnsupportedEncodingException {
        StringBuilder responseLog = new StringBuilder()
                .append("\n================= Response ==================\n")
                .append("Status    : ").append(response.getStatus()).append("\n")
                .append("Time      : ").append(time).append("\n");

        // 记录响应头（优化版）
//        responseLog.append("Headers   : \n");
//        response.getHeaderNames().forEach(header -> {
//            responseLog.append("  ")
//                    .append(header).append(": ")
//                    .append(String.join(",", response.getHeaders(header)))
//                    .append("\n");
//        });

        // 添加响应内容处理
        responseLog.append("Body      : ");

        // 检测文件类型响应
        if (isFileResponse(response)) {
            responseLog.append("[FILE RESPONSE]");
            String fileName = getFileNameFromHeaders(response);
            if (fileName != null) {
                responseLog.append(" (").append(fileName).append(")");
            }
        } else {
            // 获取响应内容（显示处理编码）
            byte[] responseBytes = ((ContentCachingResponseWrapper) response).getContentAsByteArray();
            String responseBody = new String(responseBytes, StandardCharsets.UTF_8);
            if (responseBytes.length > 0) {
                responseLog.append("\n").append(responseBody);
            } else {
                responseLog.append("[EMPTY]");
            }
        }
        responseLog.append("\n===================================================\n");

        log.info("\033[36m" + new String(responseLog.toString().getBytes(), "UTF-8") + "\033[0m");
    }

    // 判断是否为文件下载
    private boolean isFileResponse(HttpServletResponse response) {
        String contentType = response.getContentType();
        return contentType != null && (
                contentType.startsWith("application/octet-stream") ||
                        contentType.startsWith("application/pdf") ||
                        contentType.startsWith("image/") ||
                        contentType.startsWith("application/vnd.ms-excel") // Excel文件等
        );
    }

    // 从Content-Disposition头获取文件名
    private String getFileNameFromHeaders(HttpServletResponse response) {
        String contentDisposition = response.getHeader("Content-Disposition");
        if (contentDisposition != null && contentDisposition.contains("filename=")) {
            return contentDisposition.split("filename=")[1].replace("\"", "");
        }
        return null;
    }

    // 获取响应体内容
    private String getResponseBody(ContentCachingResponseWrapper response) {
        try {
            byte[] content = response.getContentAsByteArray();
            if (content.length > 0) {
                // 添加内容截断保护（限制最大长度）
                int maxLength = 2048; // 可根据需要调整
                String body = new String(content, response.getCharacterEncoding());
                return body.length() > maxLength ?
                        body.substring(0, maxLength) + "...[TRUNCATED]" :
                        body;
            }
        } catch (Exception e) {
            log.warn("Error reading response body", e);
        }
        return "";
    }



    // 修改doFilter方法
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 如果是文件上传请求，不包装请求
        if (isMultipartRequest(httpRequest)) {
            logRequest(httpRequest);
            Long start = System.currentTimeMillis();
            chain.doFilter(request, response);
            Long end = System.currentTimeMillis();
            String timed = String.valueOf(end - start);

            // 包装响应以便记录
            ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpResponse);
            responseWrapper.copyBodyToResponse();
            logResponse(responseWrapper, timed);
            return;
        }

        // 非文件上传请求，包装请求和响应
        RepeatedlyRequestWrapper requestWrapper = request instanceof RepeatedlyRequestWrapper ?
                (RepeatedlyRequestWrapper) request : new RepeatedlyRequestWrapper(httpRequest, response);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpResponse);

        try {
            logRequest(requestWrapper);
            Long start = System.currentTimeMillis();
            chain.doFilter(requestWrapper, responseWrapper);
            Long end = System.currentTimeMillis();
            String timed = String.valueOf(end - start);
            logResponse(responseWrapper, timed);
        } finally {
            responseWrapper.copyBodyToResponse();
        }
    }
}